// Copyright 2022 jeff.li. and/or its affiliates.

#pragma once

#include <tbir/runtime/object.h>
#include <tbir/runtime/runtime_value.h>

namespace tbir::runtime {

    class OpaqueObjectNode;

    typedef void (*FOpaqueObjectDeleter)(void *self);

    // OpaqueObject is used to represent a third-object, like NativeObject but more lightweight
    class OpaqueObject : public ObjectRef {
    public:
        using ContainerType = OpaqueObjectNode;
        static constexpr bool _type_is_nullable = false;  // disable nullptr for performance

    public:
        explicit OpaqueObject(::tbir::runtime::ObjectPtr<::tbir::runtime::Object> n) noexcept
                : ObjectRef(std::move(n)) {
        }

        OpaqueObject(const OpaqueObject &other) noexcept = default;

        OpaqueObject(OpaqueObject &&other) noexcept = default;

        OpaqueObject &operator=(const OpaqueObject &other) noexcept = default;

        OpaqueObject &operator=(OpaqueObject &&other) noexcept = default;

    public:
        OpaqueObject() : OpaqueObject(-1, nullptr, nullptr) {
        }

        OpaqueObject(int64_t tag, void *ptr, FOpaqueObjectDeleter deleter);

        void *GetOpaquePtr() const;

        int64_t GetTag() const;

        unsigned char *GetInternalBufferPtr() const;

        static int GetInternalBufferSize();

        void update(int64_t tag, void *ptr, FOpaqueObjectDeleter deleter) const;
    };

    namespace TypeIndex {
        template<>
        struct type_index_traits<OpaqueObject> {
            static constexpr int32_t value = kRuntimeOpaqueObject;
        };
    }  // namespace TypeIndex

    template<>
    TURBO_FORCE_INLINE OpaqueObject Any::As<OpaqueObject>() const {
        TBIR_RUNTIME_VALUE_CHECK_TYPE_CODE(value_.code, TypeIndex::kRuntimeOpaqueObject);
        return OpaqueObject(GetObjectPtr<Object>(static_cast<Object *>(value_.data.v_handle)));
    }

    template<>
    TURBO_FORCE_INLINE OpaqueObject Any::AsNoCheck<OpaqueObject>() const {
        return OpaqueObject(GetObjectPtr<Object>(static_cast<Object *>(value_.data.v_handle)));
    }

    std::ostream &operator<<(std::ostream &os, OpaqueObject const &n);

}  // namespace tbir::runtime
